package com.lfy.aop;

import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.CodeSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.concurrent.TimeUnit;

@Aspect
@Component
@Slf4j
public class LimitingAspect {
    @Resource
    RedisTemplate redisTemplate;

    @Pointcut("@annotation(limiting)")
    public void cut(Limiting limiting){
    }

    @SneakyThrows
    @Around("cut(limiting)")
    public Object LimitimgAspect(ProceedingJoinPoint joinPoint,Limiting limiting){
        //时间转换（秒）
        int time = limiting.time();
        int num = limiting.num();

        TimeUnit timeUnit = limiting.TIME_UNIT();
        long period = timeUnit.toSeconds(time);

        //当前时间(毫秒)
        long nowTime = System.currentTimeMillis();

        //获取ip、uri
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert requestAttributes != null;
        HttpServletRequest request = requestAttributes.getRequest();
        String uri = request.getRequestURI();
        String ip = getIpAddress(request);

        //key
        String key = "method_limit_".concat(uri).concat(ip);

        ZSetOperations zSetOperations = redisTemplate.opsForZSet();
        zSetOperations.add(key,nowTime,nowTime);

        //设置过期时间
        redisTemplate.expire(key,period,TimeUnit.SECONDS);

        //把限流时间之外的value删除
        zSetOperations.removeRangeByScore(key,0,nowTime-period*1000);
        //统计value(访问次数)
        Long count = zSetOperations.zCard(key);
        if(null != count && count > num){
            log.error("限流 ip:{},uri:{},method:{}.{}(),入参[k]:{},入参[v]:{}",ip,uri,joinPoint.getSignature().getDeclaringType(),
                    joinPoint.getSignature().getName(),((CodeSignature)joinPoint.getSignature()).getParameterNames(), joinPoint.getArgs());
            return "限流";
        }
        log.info("放行 ip:{},uri:{},method:{}.{}(),入参[k]:{},入参[v]:{}",ip,uri,joinPoint.getSignature().getDeclaringType(),
                joinPoint.getSignature().getName(),((CodeSignature)joinPoint.getSignature()).getParameterNames(), joinPoint.getArgs());
        return joinPoint.proceed();




    }

    private static String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }
}
